<?php
/**
 * FileSystem
 *
 * @category	FileSystem
 * @package		dayscript.filesystem
 * @subpackage
 * @author		Noel Marin <nmarin@dayscript.com>
 * @copyright	2009 Dayscript Ltda.
 * @license
 * @version		1.0
 * @version		$Revision: 0 $
 * @filesource
 * @link		http://www.dayscript.com
 * @link		{docsLink}
 * @uses
 * @since		1.0
 * @modifiedby	$LastChangedBy: Noel Marin $
 * @modified	$Date: 2009-08-28 $
 */
namespace dayscript\filesystem;

/**
 * FileSystem
 *
 * An object representation of system file
 *
 * @category	FileSystem
 * @package		dayscript.filesystem
 * @subpackage
 * @author		Noel Marin <nmarin@dayscript.com>
 * @copyright	2009 Dayscript Ltda.
 * @license
 * @version		1.0
 * @link		http://www.dayscript.com
 * @link		{docsLink}
 * @uses		\dayscript\exception\ErrorCallerException
 * @since		1.0
 * @modifiedby	$LastChangedBy: Noel Marin $
 * @modified	$Date: 2009-08-28 $
 */
class FileSystem {

	/**
	 * Complete path to the file
	 *
	 * @var string
	 */
	private $name;

	/**
	 * Type to access the file, see class constants
	 *
	 * @var string
	 */
	private $mode;

	/**
	 * File pointer resource
	 * @var resource
	 */
	private $handle;

	/**
	 * FileSystem exists or not
	 *
	 * @var boolean
	 */
	private $exist;

	/**
	 * It's TRUE if the FileSystem exists and is a directory, FALSE otherwise
	 *
	 * @var boolean
	 */
	private $directory;

	/**
	 * It's TRUE if the FileSystem exists and is readable, FALSE otherwise.
	 *
	 * @var boolean
	 */
	private $readable;

	/**
	 * It's TRUE if the FileSystem exists and is writeable, FALSE otherwise.
	 *
	 * @var boolean
	 */
	private $writeable;

	/**
	 * Open for reading only; place the file pointer at the beginning of the file.
	 * B = Beginning
	 * NC = Not Create, if doesn't exists the open method will not attempt to create it
	 * @var string
	 */
	const MODE_ONLY_READ_B_NC = "r";

	/**
	 * Open for reading and writing; place the file pointer at the beginning of the file.
	 * B = Beginning
	 * NC = Not Create, if doesn't exists the open method will not attempt to create it
	 * @var string
	 */
	const MODE_READ_WRITE_B_NC = "r+";

	/**
	 * Open for writing only; place the file pointer at the beginning of the file and truncate
	 *  the file to zero length. If the file does not exist, attempt to create it.
	 * B = Beginning
	 * C = Create, if doesn't exists the open method attempt to create it
	 * @var string
	 */
	const MODE_ONLY_WRITE_B_C = "w";

	/**
	 * Open for reading and writing; place the file pointer at the beginning of the file and
	 *  truncate the file to zero length. If the file does not exist, attempt to create it.
	 * B = Beginning
	 * C = Create, if doesn't exists the open method attempt to create it
	 * @var string
	 */
	const MODE_READ_WRITE_B_C = "w+";

	/**
	 * Open for writing only; place the file pointer at the end of the file. If the file does
	 *  not exist, attempt to create it.
	 * E = End
	 * C = Create, if doesn't exists the open method attempt to create it
	 * @var string
	 */
	const MODE_ONLY_WRITE_E_C = "a";

	/**
	 * Open for reading and writing; place the file pointer at the end of the file. If the file
	 *  does not exist, attempt to create it.
	 * E = End
	 * C = Create, if doesn't exists the open method attempt to create it
	 * @var string
	 */
	const MODE_READ_WRITE_E_C = "a+";

	/**
	 * Create and open for writing only; place the file pointer at the beginning of the file. If the
	 *  file already exists, FileSystem will fail. If the file does not exist, attempt to create it.
	 * B = Beginning
	 * C = Create, if doesn't exists the open method attempt to create it
	 * F = Fail if the file already exists
	 * @var string
	 */
	const MODE_ONLY_WRITE_B_C_F = "x";

	/**
	 * Create and open for reading and writing only; place the file pointer at the beginning of the file.
	 *  If the file already exists, FileSystem will fail. If the file does not exist, attempt to create it.
	 * B = Beginning
	 * C = Create, if doesn't exists the open method attempt to create it
	 * F = Fail if the file already exists
	 * @var string
	 */
	const MODE_READ_WRITE_B_C_F = "x+";

	/**
	 * Force binary mode, It's used for portability
	 * @var string
	 */
	const MODE_PORTABILITY = "b";

	/**
	 * Default constructor, It will create an FileSystem object
	 *
	 * @param $name
	 * @param $mode
	 * @return void
	 */
	public function __construct($name, $mode){
		if($name === "" or $name === " "){
			throw new \dayscript\exception\ErrorCallerException( "Invalid filename ", E_USER_ERROR );
		}

		if($mode === ""){
			throw new \dayscript\exception\ErrorCallerException( "Invalid mode ", E_USER_ERROR );
		}

		$this->name = $name;
		$this->mode = $mode;
		$this->handle = null;

		$this->exist = $this->exists();
		$this->directory = \is_dir($this->name);
		$this->readable = \is_readable($this->name);
		$this->writeable = \is_writable($this->name);
	}

	/**
	 * Checks whether a FileSystem exists
	 *
	 * @return boolean
	 */
	public function exists(){
		return \file_exists($this->name);
	}

	/**
	 * Return this basename, if the filename ends in suffix and suffix is set this will also be cut off of
	 * the returned value.
	 *
	 * @param $suffix
	 * @return string $baseName
	 */
	public function getBaseName($suffix = null){
		$baseName = !isset($suffix) ? \basename($this->name) : \basename($this->name, $suffix);
		return $baseName;
	}

	/**
	 * Tells whether the filename is a directory
	 *
	 * @return boolean
	 */
	public function isDirectory(){
		return $this->directory;
	}

	/**
	 * Returns FileSystem file size
	 *
	 * @return int
	 */
	public function getSize(){
		return \filesize($this->name);
	}

	/**
	 * Returns directory name component of this
	 *
	 * @return string
	 */
	public function getDirectoryName(){
		return \dirname($this->name);
	}

	/**
	 * Return the file extension if any
	 *
	 * @return string
	 */
	public function getExtension(){
		return \pathinfo($this->name, PATHINFO_EXTENSION);
	}

	/**
	 * Return the FileSystem name split by the DIRECTORY_SEPARATOR
	 *
	 * @return array
	 */
	public function getExplodeName(){
		return \explode(DIRECTORY_SEPARATOR, $this->name);
	}

	/**
	 * Returns FileSystem's permissions
	 * 
	 * @return int Permissions in an octal number
	 */
	public function getPermissions(){
		return fileperms($this->name);
	}

	/**
	 * Change the FileSystem's permissions
	 *
	 * @param $fileSystem
	 * @param $permissions Parameter must be an octal number so you need to prefix the parameter with a zero (0)
	 * @return boolean Success or not
	 */
	public function setPermissions($permissions){
		if(!$this->isExist()){
			throw new \dayscript\exception\ErrorCallerException( "File seems not exists or restriction on parent directory", E_USER_WARNING );
		}

		$name = $this->getName();

		@\chmod($name, $permissions);
		\clearstatcache(true, $name);

		$result = $this->getPermissions() == $permissions ? true : false;
		return $result;
	}

	/**
	 * Return this FileSystem name
	 *
	 * @return string
	 */
	public function getName(){
		return $this->name;
	}

	/**
	 * Set a FileSystem name
	 *
	 * @param $name
	 * @return void
	 */
	public function setName($name){
		$this->name = $name;
		$this->exist = $this->exists();
		$this->directory = \is_dir($this->name);
		$this->readable = \is_readable($this->name);
		$this->writeable = \is_writable($this->name);
	}

	/**
	 * Return this FileSystem mode
	 *
	 * @return string
	 */
	public function getMode(){
		return $this->mode;
	}

	/**
	 * Set a FileSystem name
	 *
	 * @param $mode
	 * @return void
	 */
	public function setMode($mode){
		$this->mode = $mode;
		$this->exist = $this->exists();
	}

	/**
	 * Return this FileSystem handle
	 *
	 * @return string
	 */
	public function getHandle(){
		return $this->handle;
	}

	/**
	 * Set a FileSystem handle
	 *
	 * @param $handle
	 * @return void
	 */
	public function setHandle($handle){
		$this->handle = $handle;
	}

	/**
	 * Return if the FileSystem exists or not
	 *
	 * @return boolean
	 */
	public function isExist(){
		return $this->exist;
	}

	/**
	 * Set if the FileSystem exists or not
	 *
	 * @param $exist
	 * @return void
	 */
	public function setExist($exist){
		$this->exist = $exist;
	}

	/**
	 * Return whether the filename is readable
	 *
	 * @return boolean
	 */
	public function isReadable(){
		return $this->readable;
	}

	/**
	 * Set if the FileSystem is writeable or not
	 *
	 * @param $exist
	 * @return void
	 */
	public function setReadable(){
		$this->readable = \is_readable($this->name);
	}

	/**
	 * Return whether the filename is writable
	 *
	 * @return boolean
	 */
	public function isWriteable(){
		return $this->writeable;
	}

	/**
	 * Set if the FileSystem is writeable or not
	 *
	 * @param $exist
	 * @return void
	 */
	public function setWriteable(){
		$this->writeable = \is_writable($this->name);
	}
}